// app/api/data-room/[projectId]/[fileId]/route.ts import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth/next'; import { authOptions } from '@/app/api/auth/[...nextauth]/route' import { FileService, type FileAccessContext } from '@/lib/services/fileService'; import { fileActivityLogs, fileItems } from '@/db/schema'; import { and, eq, sql } from 'drizzle-orm'; import db from "@/db/db" // 파일 정보 조회 export async function GET( request: NextRequest, { params }: { params: Promise<{ projectId: string; fileId: string }> } ) { try { const { projectId, fileId } = await params; const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: '인증이 필요합니다' }, { status: 401 }); } const context: FileAccessContext = { userId: Number(session.user.id), userDomain: session.user.domain || 'partners', userEmail: session.user.email, ipAddress: request.ip || request.headers.get('x-forwarded-for') || undefined, userAgent: request.headers.get('user-agent') || undefined, }; const fileService = new FileService(); const hasAccess = await fileService.checkFileAccess( fileId, context, 'view' ); if (!hasAccess) { return NextResponse.json( { error: '파일 접근 권한이 없습니다' }, { status: 403 } ); } // 파일 정보 반환 const file = await fileService.downloadFile(fileId, context); if (!file) { return NextResponse.json( { error: '파일을 찾을 수 없습니다' }, { status: 404 } ); } return NextResponse.json(file); } catch (error) { console.error('파일 조회 오류:', error); return NextResponse.json( { error: '파일 조회에 실패했습니다' }, { status: 500 } ); } } // 파일 수정 (이름 변경, 카테고리 변경, 파일 이동 통합) export async function PATCH( request: NextRequest, { params }: { params: Promise<{ projectId: string; fileId: string }> } ) { try { const { projectId, fileId } = await params; console.log('PATCH 요청 받음:', { projectId, fileId }); const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: '인증이 필요합니다' }, { status: 401 }); } const body = await request.json(); const { name, category, parentId, applyToChildren } = body; console.log('요청 본문:', { name, category, parentId, applyToChildren }); // 권한 확인 const fileItem = await db .select() .from(fileItems) .where( and( eq(fileItems.id, fileId), eq(fileItems.projectId, projectId) ) ) .limit(1); if (!fileItem[0]) { return NextResponse.json({ error: 'File not found' }, { status: 404 }); } // 내부 사용자만 수정 가능 if (session.user.domain === 'partners') { return NextResponse.json({ error: 'Permission denied' }, { status: 403 }); } const updateData: any = { updatedBy: session.user.id, updatedAt: new Date(), }; // 이름 변경 처리 if (name !== undefined) { // 파일인 경우 확장자 유지 if (fileItem[0].type === 'file') { const originalExt = fileItem[0].name.lastIndexOf('.'); const newNameWithoutExt = name.lastIndexOf('.') > -1 ? name.substring(0, name.lastIndexOf('.')) : name; const ext = originalExt > -1 ? fileItem[0].name.substring(originalExt) : ''; updateData.name = newNameWithoutExt + ext; } else { updateData.name = name; } // 경로 업데이트 (하위 항목들도 업데이트 필요) if (fileItem[0].type === 'folder') { const oldPath = fileItem[0].path + fileItem[0].name + '/'; const newPath = fileItem[0].path + updateData.name + '/'; // 하위 항목들의 경로 업데이트 await db .update(fileItems) .set({ path: sql`REPLACE(path, ${oldPath}, ${newPath})` }) .where( and( eq(fileItems.projectId, projectId), sql`path LIKE ${oldPath + '%'}` ) ); } } // 카테고리 변경 처리 if (category !== undefined) { updateData.category = category; // 폴더인 경우 하위 항목들도 같은 카테고리로 변경할지 옵션 제공 if (fileItem[0].type === 'folder' && applyToChildren) { await updateChildrenCategory(fileId, category, projectId); } } // 부모 폴더 변경 (파일 이동) 처리 if (parentId !== undefined) { updateData.parentId = parentId; // 새 부모 폴더의 경로 가져오기 if (parentId) { const parentFolder = await db .select() .from(fileItems) .where(eq(fileItems.id, parentId)) .limit(1); if (parentFolder[0]) { updateData.path = parentFolder[0].path + parentFolder[0].name + '/'; } } else { updateData.path = '/'; } } console.log('업데이트 데이터:', updateData); // 업데이트 실행 const [updatedItem] = await db .update(fileItems) .set(updateData) .where(eq(fileItems.id, fileId)) .returning(); // 활동 로그 기록 const action = name ? 'rename' : category ? 'category_change' : 'move'; const actionDetails: any = {}; if (name) { actionDetails.from = fileItem[0].name; actionDetails.to = updateData.name; } else if (category) { actionDetails.from = fileItem[0].category; actionDetails.to = category; } else if (parentId !== undefined) { actionDetails.from = fileItem[0].parentId; actionDetails.to = parentId; } await db.insert(fileActivityLogs).values({ fileItemId: fileId, projectId, action, actionDetails, userId: Number(session.user.id), userEmail: session.user.email, userDomain: session.user.domain, }); console.log('업데이트 완료:', updatedItem); return NextResponse.json(updatedItem); } catch (error) { console.error('파일 수정 오류:', error); return NextResponse.json( { error: '파일 수정에 실패했습니다', details: error.message }, { status: 500 } ); } } // 파일 삭제 export async function DELETE( request: NextRequest, { params }: { params: Promise<{ projectId: string; fileId: string }> } ) { try { const { projectId, fileId } = await params; const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: '인증이 필요합니다' }, { status: 401 }); } const context: FileAccessContext = { userId: session.user.id, userDomain: session.user.domain || 'partners', userEmail: session.user.email, ipAddress: request.ip || request.headers.get('x-forwarded-for') || undefined, userAgent: request.headers.get('user-agent') || undefined, }; const fileService = new FileService(); await fileService.deleteFile(fileId, context); return NextResponse.json({ success: true }); } catch (error) { if (error instanceof Error && error.message.includes('권한')) { return NextResponse.json( { error: error.message }, { status: 403 } ); } console.error('파일 삭제 오류:', error); return NextResponse.json( { error: '파일 삭제에 실패했습니다' }, { status: 500 } ); } } // 하위 항목 카테고리 업데이트 함수 async function updateChildrenCategory( parentId: string, category: string, projectId: string ) { const children = await db .select() .from(fileItems) .where( and( eq(fileItems.parentId, parentId), eq(fileItems.projectId, projectId) ) ); for (const child of children) { await db .update(fileItems) .set({ category }) .where(eq(fileItems.id, child.id)); // 재귀적으로 하위 폴더 처리 if (child.type === 'folder') { await updateChildrenCategory(child.id, category, projectId); } } }